Skip to content

MTHINC#1303

Open
wilfonba wants to merge 32 commits intoMFlowCode:masterfrom
wilfonba:MTHINC
Open

MTHINC#1303
wilfonba wants to merge 32 commits intoMFlowCode:masterfrom
wilfonba:MTHINC

Conversation

@wilfonba
Copy link
Copy Markdown
Contributor

@wilfonba wilfonba commented Mar 12, 2026

Description

This PR adds MTHINC to the code base and fixes the THINC bug in #1181. The existing golden files for THINC interface compression were preserved, and new golden files and tests were created for MTHINC.

Type of change

  • New feature

Testing

3D advection case with and without MTHINC.

test.mp4

2D advection with int_comp = 1 (THINC) 1 rank vs. 4 rank. Results are identical

test.mp4

2D advection with int_comp = 2 (MTHINC) 1 rank vs. 4 rank. Results are identical

test.mp4

3D advection with int_comp = 2 (MTHINC) 1 rank vs. 8 rank. Results are identical

I didn't bother making a video for this one, but the output was identical when compared in ParaView.

Performance tests in 3D

The following results are grind times for a 200^3 advection case run on 1 A100 with OpenACC. The overhead for MTHINC is ~3% while the overhead for THINC is ~1%. The grind times are:
(Updated 4/1/2026 after removing parameter from constants in m_thinc.fpp

  • int_comp = 0: 0.87488 (No compression, baseline)
  • int_comp = 1: 0.88406 (1%) (THINC compression)
  • int_comp = 2: 0.90199 (3%) (MTHINC compression)

--nsys results with int_comp = 2

This is the run time contribution for just the MTHINC routines.

Time (%) Total Time (ns) Instances Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name


  1.3       75,807,250         75   1,010,763.3   1,028,360.0     183,969   1,159,626    118,287.9  m_thinc_s_compute_mthinc_normals_253_gpu 
  0.5       26,689,214         75     355,856.2     355,779.0     354,243     357,475        582.9  m_thinc_s_compute_mthinc_normals_228_gpu                                               
  0.3       19,284,544         75     257,127.3     257,666.0     229,922     259,746      3,433.0  m_thinc_s_thinc_compression_316_gpu__1                                                 
  0.3       19,228,227         75     256,376.4     256,931.0     229,282     258,531      3,376.6  m_thinc_s_thinc_compression_316_gpu                                                    
  0.3       19,204,612         75     256,061.5     256,354.0     229,890     258,786      3,361.1  m_thinc_s_thinc_compression_316_gpu__2 

Checklist

  • I added or updated tests for new behavior
  • I updated documentation if user-facing behavior changed

See the developer guide for full coding standards.

GPU changes (expand if you modified src/simulation/)
  • GPU results match CPU results
  • Tested on NVIDIA GPU or AMD GPU

AI code reviews

Reviews are not triggered automatically. To request a review, comment on the PR:

  • @coderabbitai review — incremental review (new changes only)
  • @coderabbitai full review — full review from scratch
  • /review — Qodo review
  • /improve — Qodo code suggestions

@github-actions
Copy link
Copy Markdown

Claude Code Review

Head SHA: 202d7da
Files changed: 81 (key source files: m_thinc.fpp [new, +499], m_muscl.fpp [-86], m_rhs.fpp [+22], m_weno.fpp [-8], m_global_parameters.fpp, m_mpi_proxy.fpp, m_start_up.fpp, toolchain/mfc/params/definitions.py, toolchain/mfc/case_validator.py, toolchain/mfc/test/cases.py)


Summary

  • Adds a new m_thinc.fpp module implementing both THINC (1D hyperbolic tangent, existing) and MTHINC (multi-dimensional, new), consolidating what was previously split between m_muscl.fpp and m_weno.fpp
  • Promotes int_comp from logical to integer (0=off, 1=THINC, 2=MTHINC) — a breaking type change for existing users
  • Fixes a pre-existing THINC bug (Fix MUSCL THINC right-state using already-overwritten left-state values #1181) where the right-reconstruction density ratios used already-overwritten left-reconstruction values; new code pre-saves rho_b/rho_e before any write
  • Moves compression call from s_weno/s_muscl to the shared s_reconstruct_cell_boundary_values in m_rhs.fpp, enabling THINC/MTHINC with any reconstruction type
  • MTHINC normals are pre-computed once per timestep in s_compute_mthinc_normals before the dimensional splitting loop — correct design

Findings

[HIGH] Missing Fortran-side runtime validation for int_comp
m_checker.fpp / m_checker_common.fpp have no @:PROHIBIT guard for invalid int_comp values or for int_comp=2 with 1D (n=0). Per project convention (CLAUDE.md, parameter-system.md), Fortran-side checks belong in m_checker*.fpp. A user passing int_comp=3 would fail only at Python validation, not at simulation startup. Suggested:

@:PROHIBIT(int_comp < 0 .or. int_comp > 2, "int_comp must be 0, 1, or 2")

[HIGH] int_comp=2 silently degrades to 1D THINC for 1D cases
m_rhs.fpp line 286 and m_thinc.fpp ~line 741 both gate MTHINC on n > 0, so a user who sets int_comp=2 on a 1D case gets quiet THINC behaviour with no warning. This should either be prohibited or documented. Currently neither the validator nor the checker warns.

[MEDIUM] Backward compatibility break: int_comp type change (logical → integer)
toolchain/mfc/params/definitions.py moves int_comp from the LOG group to INT. Existing case files with 'int_comp': True or 'int_comp': False will fail schema validation (fastjsonschema rejects bool for integer fields). A migration note or compatibility shim would help existing users.

[MEDIUM] Removed constraint on int_comp requiring MUSCL
case_validator.py deletes the check that int_comp and recon_type != 2. THINC with WENO is now actively tested and apparently intentional. However, no constraint guards against int_comp > 0 with recon_type=3 (first-order/piecewise-constant). Applying THINC sharpening on top of a piecewise-constant reconstruction seems physically questionable — worth a comment or prohibition.

[MEDIUM] f_mthinc_solve_d Newton iteration unbounded in d
m_thinc.fpp lines 532-541: d is unconstrained during iteration. For degenerate normals or extreme alpha_cell values, d can drift to large magnitudes, causing tanh(beta*(n*xi + d)) -> +/-1 and dV -> 0. The guard if (abs(dV) < 1e-14_wp) exit catches this, but the stored d is unvalidated. Consider clamping d to [-d_max, d_max] for some d_max (e.g., 10/beta).

[LOW] Hardcoded 1e-14_wp convergence thresholds in f_mthinc_solve_d (lines 536, 538) should use a named constant or verysmall consistent with MFC convention.

[LOW] Redundant precision cast m_thinc.fpp line 670: real(ac, wp)ac is already real(wp).

[LOW] MTHINC not actually exercised in some new tests
cases.py line 188: for int_comp in [0, 1, 2] with muscl_order=1. If the base case for alter_muscl is 1D, then int_comp=2 silently runs as 1D THINC (per HIGH finding above), meaning the MTHINC code path is not exercised in those tests.

@wilfonba wilfonba marked this pull request as ready for review March 12, 2026 15:52
@wilfonba wilfonba requested a review from sbryngelson as a code owner March 12, 2026 15:52
Copilot AI review requested due to automatic review settings March 12, 2026 15:52
@qodo-code-review
Copy link
Copy Markdown
Contributor

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Review Summary by Qodo

Add MTHINC interface compression and refactor THINC functionality

✨ Enhancement 🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Implements MTHINC (multi-dimensional THINC) interface compression for volume fraction sharpening
  alongside existing THINC support
• Introduces new m_thinc module with helper functions for stable logarithmic-cosine computation,
  1D THINC integrals, and multi-dimensional volume integrals using Gaussian quadrature
• Converts int_comp parameter from logical to integer type with three states: 0=off, 1=THINC,
  2=MTHINC
• Integrates THINC/MTHINC compression into RHS computation with Newton iteration for interface
  position parameter solving
• Refactors interface compression functionality out of MUSCL module into dedicated m_thinc module
• Updates MPI communication to handle new integer parameter type
• Adds comprehensive test cases for MTHINC in 2D/3D configurations with WENO reconstruction
• Updates all example cases and parameter definitions to use new integer enum values
• Includes performance validation showing ~3% overhead for MTHINC vs ~1% for THINC
• Updates documentation and validation logic for the new parameter type and options
Diagram
flowchart LR
  A["int_comp: logical<br/>to integer"] --> B["m_thinc module<br/>THINC + MTHINC"]
  B --> C["RHS computation<br/>with compression"]
  B --> D["Startup initialization<br/>and finalization"]
  C --> E["Test cases<br/>2D/3D MTHINC"]
  A --> F["MPI broadcast<br/>parameter update"]
  A --> G["Parameter validation<br/>0/1/2 values"]
Loading

Grey Divider

File Changes

1. src/simulation/m_thinc.fpp ✨ Enhancement +499/-0

New THINC and MTHINC interface compression module

• New module implementing THINC (1D) and MTHINC (multi-dimensional) interface compression for volume
 fraction sharpening
• Contains helper functions for stable logarithmic-cosine computation, 1D THINC integrals, and
 multi-dimensional volume integrals with Gaussian quadrature
• Implements Newton iteration to solve for interface position parameter d that preserves
 cell-averaged volume fractions
• Provides subroutines to initialize/finalize MTHINC data structures, compute unit normals and
 interface positions, and apply compression to reconstructed variables

src/simulation/m_thinc.fpp


2. src/simulation/m_muscl.fpp Refactoring +1/-86

Remove interface compression from MUSCL module

• Removed s_interface_compression from public interface exports
• Interface compression functionality moved to dedicated m_thinc module

src/simulation/m_muscl.fpp


3. src/simulation/m_rhs.fpp ✨ Enhancement +22/-1

Integrate THINC/MTHINC compression into RHS computation

• Added import of m_thinc module
• Added call to s_compute_mthinc_normals before dimensional splitting loop when int_comp == 2
• Integrated s_thinc_compression call after variable reconstruction with NVTX profiling markers
• Renamed NVTX range from "RHS-WENO" to "RHS-RECONSTRUCTION" for clarity

src/simulation/m_rhs.fpp


View more (78)
4. src/simulation/m_mpi_proxy.fpp 🐞 Bug fix +2/-2

Update MPI broadcast for int_comp parameter type change

• Added int_comp to integer broadcast list for MPI communication
• Removed int_comp from logical broadcast list (changed from boolean to integer type)

src/simulation/m_mpi_proxy.fpp


5. src/simulation/m_global_parameters.fpp ✨ Enhancement +3/-3

Change interface compression parameter to integer enum

• Changed int_comp from logical to integer type with values: 0=off, 1=THINC, 2=MTHINC
• Updated default value from .false. to 0
• Updated documentation comments for ic_beta to reference both THINC and MTHINC

src/simulation/m_global_parameters.fpp


6. src/simulation/m_start_up.fpp ✨ Enhancement +3/-0

Initialize and finalize THINC module in startup

• Added import of m_thinc module
• Added initialization call s_initialize_thinc_module() when int_comp > 0
• Added finalization call s_finalize_thinc_module() when int_comp > 0

src/simulation/m_start_up.fpp


7. toolchain/mfc/test/cases.py 🧪 Tests +8/-2

Add MTHINC test cases and update parameter values

• Added MTHINC testing for WENO cases with weno_order = 5 in 2D/3D configurations
• Updated MUSCL test cases to use integer values [0, 1, 2] for int_comp instead of boolean strings

toolchain/mfc/test/cases.py


8. examples/1D_sodshocktube_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/1D_sodshocktube_muscl/case.py


9. examples/2D_riemann_test_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/2D_riemann_test_muscl/case.py


10. examples/2D_advection_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/2D_advection_muscl/case.py


11. examples/2D_shockdroplet_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/2D_shockdroplet_muscl/case.py


12. examples/3D_shockdroplet_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/3D_shockdroplet_muscl/case.py


13. examples/3D_rayleigh_taylor_muscl/case.py ⚙️ Configuration changes +1/-1

Update example case parameter to integer

• Updated int_comp parameter from string "T" to integer 1 (THINC)

examples/3D_rayleigh_taylor_muscl/case.py


14. toolchain/mfc/params/definitions.py ⚙️ Configuration changes +2/-1

Update parameter type definition for int_comp

• Moved int_comp from logical parameter list to integer parameter list
• Changed parameter type from LOG to INT

toolchain/mfc/params/definitions.py


15. toolchain/mfc/case_validator.py 🐞 Bug fix +3/-3

Update validation for integer int_comp parameter

• Updated validation logic to check int_comp as integer with valid values [0, 1, 2]
• Changed constraint message to clarify MTHINC option and removed requirement for MUSCL-only usage

toolchain/mfc/case_validator.py


16. toolchain/mfc/params/descriptions.py 📝 Documentation +1/-1

Update parameter description for int_comp

• Updated parameter description to reflect integer type with options: 0=off, 1=THINC, 2=MTHINC

toolchain/mfc/params/descriptions.py


17. tests/6102D509/golden-metadata.txt Miscellaneous +38/-67

Update golden test metadata

• Updated test metadata with new Git commit hash and branch name
• Updated CMake and compiler versions in configuration records

tests/6102D509/golden-metadata.txt


18. tests/6775D763/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/6775D763/golden-metadata.txt


19. tests/A438B8CE/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/A438B8CE/golden-metadata.txt


20. tests/AEE7A791/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/AEE7A791/golden-metadata.txt


21. tests/CE35B602/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/CE35B602/golden-metadata.txt


22. tests/F5FABAE9/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/F5FABAE9/golden-metadata.txt


23. tests/A930AE61/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/A930AE61/golden-metadata.txt


24. tests/C420EDF3/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MTHINC test case

tests/C420EDF3/golden-metadata.txt


25. tests/885D5D8C/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MUSCL interface compression test case

tests/885D5D8C/golden-metadata.txt


26. tests/8C42A56C/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MUSCL interface compression test case

tests/8C42A56C/golden-metadata.txt


27. tests/FE9379D8/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MUSCL interface compression test case

tests/FE9379D8/golden-metadata.txt


28. tests/E11FD23A/golden-metadata.txt 🧪 Tests +154/-0

Add new golden test metadata

• New golden test metadata file for MUSCL interface compression test case

tests/E11FD23A/golden-metadata.txt


29. docs/documentation/case.md 📝 Documentation +2/-2

Update documentation for int_comp parameter

• Updated int_comp parameter documentation from logical to integer type
• Added clarification that values [1] THINC and [2] MTHINC are supported
• Updated description to indicate interface compression works with variable reconstruction

docs/documentation/case.md


30. docs/module_categories.json 📝 Documentation +2/-1

Add THINC module to documentation categories

• Added m_thinc module to the reconstruction module category

docs/module_categories.json


31. tests/A438B8CE/golden.txt 🧪 Tests +16/-0

New MTHINC golden test data file

• Added new golden file with 16 lines of test data for MTHINC interface compression
• Contains conservative and primitive variable outputs (cons and prim) across 4 variables
• Includes initial state (timestep 000000) and evolved state (timestep 000050) for validation
• Data represents a 3D advection test case with multiple spatial regions and varying values

tests/A438B8CE/golden.txt


32. tests/F5FABAE9/golden.txt 🧪 Tests +16/-0

New MTHINC golden file test data

• Added new golden file with 16 lines of test data for MTHINC interface compression
• Contains initial and evolved state data for 5 conserved/primitive variables across 320 grid points
• Includes two timesteps (000000 and 000050) showing solution evolution
• Data demonstrates compression results with values ranging from near-zero to 2.5

tests/F5FABAE9/golden.txt


33. src/simulation/m_weno.fpp Additional files +0/-8

...

src/simulation/m_weno.fpp


34. tests/09195EF4/golden-metadata.txt Additional files +0/-0

...

tests/09195EF4/golden-metadata.txt


35. tests/09195EF4/golden.txt Additional files +0/-0

...

tests/09195EF4/golden.txt


36. tests/0A362971/golden-metadata.txt Additional files +0/-160

...

tests/0A362971/golden-metadata.txt


37. tests/0A362971/golden.txt Additional files +0/-10

...

tests/0A362971/golden.txt


38. tests/11D609F6/golden-metadata.txt Additional files +0/-0

...

tests/11D609F6/golden-metadata.txt


39. tests/11D609F6/golden.txt Additional files +0/-0

...

tests/11D609F6/golden.txt


40. tests/1B300F28/golden-metadata.txt Additional files +0/-189

...

tests/1B300F28/golden-metadata.txt


41. tests/1F0C4A44/golden-metadata.txt Additional files +0/-0

...

tests/1F0C4A44/golden-metadata.txt


42. tests/1F0C4A44/golden.txt Additional files +0/-0

...

tests/1F0C4A44/golden.txt


43. tests/2C9844EF/golden-metadata.txt Additional files +0/-156

...

tests/2C9844EF/golden-metadata.txt


44. tests/2C9844EF/golden.txt Additional files +0/-24

...

tests/2C9844EF/golden.txt


45. tests/2D11A034/golden-metadata.txt Additional files +0/-0

...

tests/2D11A034/golden-metadata.txt


46. tests/2D11A034/golden.txt Additional files +0/-0

...

tests/2D11A034/golden.txt


47. tests/2E4AC44C/golden-metadata.txt Additional files +0/-0

...

tests/2E4AC44C/golden-metadata.txt


48. tests/2E4AC44C/golden.txt Additional files +0/-0

...

tests/2E4AC44C/golden.txt


49. tests/3ECA875A/golden-metadata.txt Additional files +0/-0

...

tests/3ECA875A/golden-metadata.txt


50. tests/3ECA875A/golden.txt Additional files +0/-0

...

tests/3ECA875A/golden.txt


51. tests/409B55DF/golden-metadata.txt Additional files +0/-0

...

tests/409B55DF/golden-metadata.txt


52. tests/409B55DF/golden.txt Additional files +0/-0

...

tests/409B55DF/golden.txt


53. tests/503EEFF7/golden-metadata.txt Additional files +0/-0

...

tests/503EEFF7/golden-metadata.txt


54. tests/503EEFF7/golden.txt Additional files +0/-0

...

tests/503EEFF7/golden.txt


55. tests/50EC2239/golden-metadata.txt Additional files +0/-176

...

tests/50EC2239/golden-metadata.txt


56. tests/50EC2239/golden.txt Additional files +0/-18

...

tests/50EC2239/golden.txt


57. tests/6102D509/golden.txt Additional files +10/-0

...

tests/6102D509/golden.txt


58. tests/6775D763/golden.txt Additional files +12/-0

...

tests/6775D763/golden.txt


59. tests/67C777D8/golden.txt Additional files +0/-506

...

tests/67C777D8/golden.txt


60. tests/7156050E/golden-metadata.txt Additional files +0/-0

...

tests/7156050E/golden-metadata.txt


61. tests/7156050E/golden.txt Additional files +0/-0

...

tests/7156050E/golden.txt


62. tests/845DC70C/golden-metadata.txt Additional files +0/-0

...

tests/845DC70C/golden-metadata.txt


63. tests/845DC70C/golden.txt Additional files +0/-0

...

tests/845DC70C/golden.txt


64. tests/885D5D8C/golden.txt Additional files +10/-20

...

tests/885D5D8C/golden.txt


65. tests/8876692F/golden-metadata.txt Additional files +0/-160

...

tests/8876692F/golden-metadata.txt


66. tests/8876692F/golden.txt Additional files +0/-14

...

tests/8876692F/golden.txt


67. tests/8C42A56C/golden.txt Additional files +12/-0

...

tests/8C42A56C/golden.txt


68. tests/A930AE61/golden.txt Additional files +12/-0

...

tests/A930AE61/golden.txt


69. tests/AEE7A791/golden.txt Additional files +0/-0

...

tests/AEE7A791/golden.txt


70. tests/B3C724B5/golden-metadata.txt Additional files +0/-0

...

tests/B3C724B5/golden-metadata.txt


71. tests/B3C724B5/golden.txt Additional files +0/-0

...

tests/B3C724B5/golden.txt


72. tests/B903D17E/golden-metadata.txt Additional files +0/-0

...

tests/B903D17E/golden-metadata.txt


73. tests/B903D17E/golden.txt Additional files +12/-0

...

tests/B903D17E/golden.txt


74. tests/C02D71EE/golden-metadata.txt Additional files +0/-176

...

tests/C02D71EE/golden-metadata.txt


75. tests/C02D71EE/golden.txt Additional files +0/-18

...

tests/C02D71EE/golden.txt


76. tests/C420EDF3/golden.txt Additional files +12/-0

...

tests/C420EDF3/golden.txt


77. tests/CE35B602/golden.txt Additional files +10/-0

...

tests/CE35B602/golden.txt


78. tests/E11FD23A/golden.txt Additional files +10/-0

...

tests/E11FD23A/golden.txt


79. tests/FA4D8FEF/golden-metadata.txt Additional files +0/-189

...

tests/FA4D8FEF/golden-metadata.txt


80. tests/FA4D8FEF/golden.txt Additional files +0/-32

...

tests/FA4D8FEF/golden.txt


81. tests/FE9379D8/golden.txt Additional files +12/-0

...

tests/FE9379D8/golden.txt


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Mar 12, 2026

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (2)

Grey Divider


Action required

1. Compression skipped on split recon 🐞 Bug ≡ Correctness ⭐ New
Description
s_thinc_compression indexes reconstruction arrays using global eqn_idx%cont/%adv indices, but
reconstruction is frequently invoked on reindexed slices of the variable vector; the new guard
v_size >= eqn_idx%adv%end causes interface compression to be silently skipped in common
split-reconstruction paths (e.g., viscous/surface-tension branches), meaning int_comp has no
effect in those runs.
Code

src/simulation/m_weno.fpp[R1386-1395]

+        if (int_comp > 0 .and. v_size >= eqn_idx%adv%end) then
+            call nvtxStartRange("WENO-INTCOMP")
+            #:for WENO_DIR, XYZ in [(1, 'x'), (2, 'y'), (3, 'z')]
+                if (weno_dir == ${WENO_DIR}$) then
+                    call s_thinc_compression(v_rs_ws_${XYZ}$, vL_rs_vf_x, vL_rs_vf_y, vL_rs_vf_z, vR_rs_vf_x, vR_rs_vf_y, &
+                                             & vR_rs_vf_z, weno_dir, is1_weno, is2_weno, is3_weno)
+                end if
+            #:endfor
+            call nvtxEndRange()
        end if
Evidence
m_rhs reconstructs in multiple calls for some paths (e.g., viscous: first cont vars, then
E..sys_size), passing v_vf(iv%beg:iv%end) into WENO/MUSCL. Those assumed-shape slices are
reindexed from 1 in the callee, but s_thinc_compression still reads/writes using global eqn_idx
variable indices. The added v_size >= eqn_idx%adv%end guard prevents the call in split cases
(since v_size is the slice length), effectively disabling compression when cont and adv are not
reconstructed in the same call.

src/simulation/m_weno.fpp[1386-1395]
src/simulation/m_rhs.fpp[657-670]
src/simulation/m_rhs.fpp[1609-1648]
src/simulation/m_thinc.fpp[294-360]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Interface compression is called from WENO/MUSCL on the reconstruction-time variable slice, but `s_thinc_compression` uses global `eqn_idx` indices (cont/adv) to index into those slice-local arrays. The current workaround guard (`v_size >= eqn_idx%adv%end`) prevents out-of-bounds, but also disables interface compression in split reconstruction paths (e.g., viscous/surface-tension branches in `m_rhs`).

### Issue Context
`m_rhs` reconstructs different variable ranges in separate calls depending on physics settings; interface compression needs cont+adv together (and needs correct indexing) to function.

### Fix Focus Areas
- src/simulation/m_weno.fpp[1386-1395]
- src/simulation/m_muscl.fpp[213-221]
- src/simulation/m_rhs.fpp[657-696]
- src/simulation/m_rhs.fpp[1609-1648]
- src/simulation/m_thinc.fpp[294-411]

### Recommended fix options (pick one)
1) **Move compression to `m_rhs` (preferred for correctness):**
  - Always reconstruct cont+adv in the same call when `int_comp>0` (even if other variables are reconstructed separately).
  - Then call `s_thinc_compression` with full (global-indexed) arrays, avoiding slice reindexing entirely.

2) **Make `s_thinc_compression` slice-aware:**
  - Pass an `iv_beg` (global offset) into WENO/MUSCL and then into `s_thinc_compression`.
  - Compute local indices: `adv_local = eqn_idx%adv%beg - iv_beg + 1`, similarly for `cont_local`.
  - Use these local indices for `v_rs_ws` and `vL/vR` access.
  - Update the call guard to check presence via offsets rather than `v_size >= eqn_idx%adv%end`.

Add/extend a test where `Re_size != 0` (or surface tension path) and `int_comp=1/2` to ensure compression is actually applied (not silently skipped).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. MTHINC normals ignore spacing 🐞 Bug ≡ Correctness
Description
s_compute_mthinc_normals computes the interface normal using unscaled index-space differences, so on
nonuniform/stretched grids the normal direction is wrong and MTHINC face averages are computed for
the wrong orientation. This can silently distort interface sharpening and fluxes in real runs
without crashing.
Code

src/simulation/m_thinc.fpp[R246-259]

+                    ac = v_vf(eqn_idx%adv%beg)%sf(j, k, l)
+
+                    if (ac >= ic_eps .and. ac <= 1._wp - ic_eps) then
+                        nr_x = (v_vf(eqn_idx%adv%beg)%sf(j + 1, k, l) - v_vf(eqn_idx%adv%beg)%sf(j - 1, k, l))*5e-1_wp
+
+                        nr_y = 0._wp
+                        if (n > 0) then
+                            nr_y = (v_vf(eqn_idx%adv%beg)%sf(j, k + 1, l) - v_vf(eqn_idx%adv%beg)%sf(j, k - 1, l))*5e-1_wp
+                        end if
+
+                        nr_z = 0._wp
+                        if (p > 0) then
+                            nr_z = (v_vf(eqn_idx%adv%beg)%sf(j, k, l + 1) - v_vf(eqn_idx%adv%beg)%sf(j, k, l - 1))*5e-1_wp
+                        end if
Evidence
The new normal computation uses centered differences multiplied by 0.5 with no dx/dy/dz (or
coordinate distance) scaling, while other physics in the codebase explicitly divides by dx/dy/dz
when forming gradients—indicating grid spacing is expected to matter.

src/simulation/m_thinc.fpp[246-259]
src/simulation/m_bubbles_EE.fpp[100-121]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/simulation/m_thinc.fpp:s_compute_mthinc_normals` computes MTHINC interface normals using raw index-space differences (no dx/dy/dz or coordinate-distance scaling). On nonuniform grids this produces incorrect normal directions and therefore incorrect MTHINC face-averaged volume fractions.
### Issue Context
Other gradient computations in the simulation divide by `dx`, `dy`, `dz`, so the codebase expects physical-space gradients.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[246-283]
### What to change
- Replace the current centered differences with metric-scaled differences, e.g.:
- `nr_x = (alpha(j+1)-alpha(j-1)) / (x_cc(j+1)-x_cc(j-1))` (or `/ (dx(j)+dx(j-1))` depending on grid definitions)
- same for y/z using `y_cc`/`dy`, `z_cc`/`dz`.
- Keep the normalization to unit length afterward.
- Ensure the chosen metric works for cylindrical/axisymmetric setups if supported by the rest of the solver.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. int_comp absent on device 🐞 Bug ≡ Correctness
Description
In m_thinc:s_thinc_compression, the OpenACC-parallel kernel branches on int_comp (THINC vs MTHINC)
under default(present), but int_comp is not declared/created for the device in m_global_parameters,
risking an OpenACC runtime 'not present' failure in GPU builds. This risk is introduced because
int_comp is now referenced inside device code instead of only host-side conditionals.
Code

src/simulation/m_thinc.fpp[R380-383]

+                            if (aC >= ic_eps .and. aC <= 1._wp - ic_eps) then
+
+                                if (int_comp == 2 .and. n > 0) then
+                                    ! MTHINC: multi-dimensional face averages
Evidence
The project’s OpenACC loop macro defaults to default(present), meaning variables used inside the
GPU region must already be present on the device. The new THINC kernel references int_comp inside
the GPU loop, but the global parameter module’s GPU_DECLARE lists do not include int_comp, so
there is no clear path that makes it device-present.

src/common/include/acc_macros.fpp[132-158]
src/simulation/m_thinc.fpp[356-384]
src/simulation/m_global_parameters.fpp[205-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`int_comp` is referenced inside OpenACC GPU kernels under `default(present)`, but it is not explicitly declared/created on the device. This can produce OpenACC runtime failures (variable not present) in GPU builds.
### Issue Context
- `ACC_PARALLEL_LOOP` defaults to `default(present)`.
- `m_thinc:s_thinc_compression` branches on `int_comp` inside `$:GPU_PARALLEL_LOOP`.
- `m_global_parameters` contains the central `GPU_DECLARE` lists for globals, but does not include `int_comp`.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[356-384]
- src/simulation/m_global_parameters.fpp[205-216]
- src/common/include/acc_macros.fpp[132-158]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. vR densities use vL 🐞 Bug ≡ Correctness
Description
In m_thinc:s_thinc_compression (THINC path), rho_b and rho_e are computed only from vL_rs_vf_*
ratios and then applied to overwrite both vL_rs_vf_* and vR_rs_vf_* densities, discarding the
pre-compression vR reconstructed density/alpha ratios. Since WENO/MUSCL compute vL and vR using
different polynomials, this can make the right-face state inconsistent and distort fluxes.
Code

src/simulation/m_thinc.fpp[R453-473]

+                                        ! Save original density ratios before THINC overwrites them
+                                        rho_b = vL_rs_vf_${XYZ}$ (j, k, l, contxb)/vL_rs_vf_${XYZ}$ (j, k, l, advxb)
+                                        rho_e = vL_rs_vf_${XYZ}$ (j, k, l, contxe)/(1._wp - vL_rs_vf_${XYZ}$ (j, k, l, advxb))
+
+                                        ! Left reconstruction
+                                        aTHINC = qmin + 5e-1_wp*qmax*(1._wp + sgn*A)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxb) = rho_b*aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxe) = rho_e*(1._wp - aTHINC)
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
+
+                                        ! Right reconstruction
+                                        aTHINC = qmin + 5e-1_wp*qmax*(1._wp + sgn*(tanh(beta_eff) + A)/(1._wp + A*tanh(beta_eff)))
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxb) = rho_b*aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxe) = rho_e*(1._wp - aTHINC)
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
Evidence
The THINC branch explicitly derives density ratios solely from vL and then uses those same ratios to
set vR values. Separately, WENO reconstruction computes vL and vR using different coefficient sets
(cbL vs cbR), so vR generally differs from vL before compression; overwriting vR with vL-derived
ratios ignores that reconstruction.

src/simulation/m_thinc.fpp[453-473]
src/simulation/m_weno.fpp[729-766]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In the THINC (1D) compression path, right-face densities (`vR_rs_vf_*`) are overwritten using density ratios computed from the left-face reconstruction (`vL_rs_vf_*`). This discards the right-face reconstructed density/alpha ratios and can make the right state inconsistent.
### Issue Context
WENO/MUSCL reconstruct `vL` and `vR` with different coefficient sets, so they are not generally identical before compression. THINC should preserve each face’s density ratio (or use a consistent cell-averaged ratio) when updating densities alongside alpha.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[453-473]
- src/simulation/m_weno.fpp[729-766]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Multi-fluid THINC corruption 🐞 Bug ≡ Correctness
Description
s_thinc_compression updates only contxb/contxe and advxb/advxe (and forces `advxe = 1 -
aTHINC), which is only valid when num_fluids == 2`; with more fluids it will violate
volume-fraction normalization and corrupt partial densities. The case validator currently allows
int_comp values without checking num_fluids, so misconfigured multi-fluid runs won’t be blocked.
Code

src/simulation/m_thinc.fpp[R348-369]

+                                        rho1 = v_rs_ws(j, k, l, contxb)/aC
+                                        rho2 = v_rs_ws(j, k, l, contxe)/(1._wp - aC)
+
+                                        ! Left face (face_pos = -0.5)
+                                        aTHINC = f_mthinc_face_average(nh1, nh2, nh3, d_local, ic_beta, ${REC_DIR}$, -5e-1_wp, &
+                                                                       & num_dims)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxb) = rho1*aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxe) = rho2*(1._wp - aTHINC)
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
+
+                                        ! Right face (face_pos = +0.5)
+                                        aTHINC = f_mthinc_face_average(nh1, nh2, nh3, d_local, ic_beta, ${REC_DIR}$, 5e-1_wp, &
+                                                                       & num_dims)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxb) = rho1*aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxe) = rho2*(1._wp - aTHINC)
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
Evidence
For non-IGR volume-fraction model runs, MFC stores **N** volume fractions for **N** fluids (not
N−1). However, THINC/MTHINC compression only overwrites the first and last
volume-fraction/continuity slots, leaving any intermediate fluids untouched and breaking the
sum-to-one constraint. The Python validator introduced/updated for integer int_comp does not
restrict this usage to num_fluids==2, so users can reach this incorrect code path.

src/simulation/m_global_parameters.fpp[889-904]
src/simulation/m_thinc.fpp[323-369]
toolchain/mfc/case_validator.py[370-384]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`int_comp` (THINC/MTHINC) compression currently assumes exactly 2 fluids by writing only `contxb/contxe` and `advxb/advxe` and setting `advxe = 1 - aTHINC`. When `num_fluids > 2`, this silently corrupts reconstructed state (volume fractions no longer sum to 1 and intermediate fluids are inconsistent).
### Issue Context
- `m_global_parameters` stores `num_fluids` volume fractions when `igr = F`.
- `s_thinc_compression` only updates the first+last fluid slots.
- The case validator does not prohibit `int_comp > 0` for `num_fluids != 2`.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[370-384]
- src/simulation/m_thinc.fpp[323-412]
- src/simulation/m_start_up.fpp[926-931]
### Recommended fix
1. In `check_interface_compression`, fetch `num_fluids` and **prohibit** `int_comp > 0` unless `num_fluids == 2` (and optionally `model_eqns` is a volume-fraction-capable model).
2. Add a Fortran-side runtime guard (e.g., in startup/checker) that errors out if `int_comp > 0` and `num_fluids /= 2`, to protect non-toolchain execution paths.
3. (Optional) If multi-fluid compression is desired, generalize the algorithm to update all `adv_idx` and all `cont_idx` consistently rather than hard-coding `contxb/contxe` and `advxb/advxe`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. m_thinc indentation not 2-space 📘 Rule violation ⚙ Maintainability
Description
The newly added m_thinc module uses 4-space indentation, which conflicts with the required 2-space
indentation style. This reduces consistency and makes the codebase harder to maintain per the
project’s formatting conventions.
Code

src/simulation/m_thinc.fpp[R16-26]

+    use m_derived_types
+    use m_global_parameters
+    use m_helper
+
+#ifdef MFC_OpenACC
+    use openacc
+#endif
+
+    implicit none
+
+    private; public :: s_initialize_thinc_module, s_thinc_compression, s_compute_mthinc_normals, s_finalize_thinc_module
Evidence
PR Compliance ID 8 requires 2-space indentation for new/modified Fortran code. In the added
src/simulation/m_thinc.fpp module, statements (e.g., use ..., implicit none, and `private;
public :: ...`) are indented with 4 spaces.

CLAUDE.md
src/simulation/m_thinc.fpp[16-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Fortran module `src/simulation/m_thinc.fpp` uses 4-space indentation, but the style guide requires 2-space indentation.
## Issue Context
This PR introduces the new `m_thinc` module; as a new file, its formatting should match the required conventions.
## Fix Focus Areas
- src/simulation/m_thinc.fpp[16-26]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. THINC out-of-bounds risk 🐞 Bug ☼ Reliability
Description
s_thinc_compression unconditionally reads v_rs_ws(j±1,...) but the simulation executable reads
simulation.inp directly and does not enforce the toolchain validator’s constraints, so a user can
enable int_comp with first-order reconstruction and trigger out-of-bounds reads. This can cause
crashes or undefined behavior in release builds (no bounds checking).
Code

src/simulation/m_thinc.fpp[R316-319]

+                        do j = is1_d%beg, is1_d%end
+                            aCL = v_rs_ws(j - 1, k, l, eqn_idx%adv%beg)
+                            aC = v_rs_ws(j, k, l, eqn_idx%adv%beg)
+                            aCR = v_rs_ws(j + 1, k, l, eqn_idx%adv%beg)
Evidence
The new compression kernel reads j-1/j+1. Reconstruction bounds are shrunk by *_polyn; if
*_polyn==0 (first-order), bounds are not shrunk, making j±1 potentially outside the allocated
buffer. The Python validator prohibits int_comp != 0 with order-1 schemes, but the Fortran
simulation reads the namelist without enforcing those constraints, so the invalid configuration is
reachable by running the binary directly.

src/simulation/m_thinc.fpp[316-320]
src/simulation/m_rhs.fpp[1618-1633]
src/simulation/m_start_up.fpp[75-128]
toolchain/mfc/case_validator.py[386-402]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`s_thinc_compression` assumes a stencil and reads `v_rs_ws(j-1,...)` and `v_rs_ws(j+1,...)` for every `j` in `is1_d`. If the reconstruction polynomial degree is 0 (WENO/MUSCL order 1), `is1_d` is not shrunk and `j±1` can go out-of-bounds.
### Issue Context
The Python validator blocks `int_comp` with order-1 schemes, but `simulation` reads `simulation.inp` directly and does not enforce the same constraints, so users can still hit this path.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[294-411]
- src/simulation/m_rhs.fpp[1618-1633]
### What to change
Implement a Fortran-side safety guard, e.g. one of:
1) Early return from `s_thinc_compression` when the active reconstruction has `*_polyn == 0`.
2) Tighten the j-loop bounds inside `s_thinc_compression` to avoid endpoints (e.g., iterate `j = is1_d%beg+1, is1_d%end-1` when needed).
3) Add an explicit runtime `s_mpi_abort`/`s_prohibit_abort` check during startup validating that `int_comp>0` implies reconstruction order > 1.
Prefer (1) or (3) if order-1 + compression is unsupported by design; prefer (2) if you want to allow it safely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Missing precheck/build/test evidence 📘 Rule violation ≡ Correctness
Description
The PR description does not document running ./mfc.sh precheck, ./mfc.sh build, or relevant
automated tests after adding a new Fortran module and changing parameter typing. Without this, CI
readiness and correctness of the change cannot be confirmed from the PR.
Code

src/simulation/m_thinc.fpp[R7-14]

+!> @brief THINC and MTHINC interface compression for volume fraction sharpening.
+!! THINC (int_comp=1): 1D directional interface compression applied after
+!! MUSCL/WENO reconstruction. Uses hyperbolic tangent profile per dimension.
+!! MTHINC (int_comp=2): Multi-dimensional THINC that reconstructs a tanh
+!! profile oriented along the interface normal (computed from the gradient
+!! of alpha), then integrates that profile over each cell face using
+!! Gaussian quadrature. A Newton iteration enforces the conservation
+!! constraint (cell-averaged alpha is preserved).
Evidence
Compliance ID 1 requires precheck, build, and relevant tests to be run and pass. The PR description
lists manual case runs/perf results but does not mention running the required commands, while the
diff shows substantial new Fortran functionality and a parameter type change that should be covered
by those checks.

CLAUDE.md
src/simulation/m_thinc.fpp[7-14]
toolchain/mfc/params/definitions.py[983-986]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR does not provide evidence that required CI-equivalent checks were run (`./mfc.sh precheck`, `./mfc.sh build`, and relevant tests).
## Issue Context
This PR adds a new Fortran module for THINC/MTHINC and changes the `int_comp` parameter type/handling; these changes should be validated by precheck/build and appropriate tests.
## Fix Focus Areas
- src/simulation/m_thinc.fpp[7-14]
- toolchain/mfc/params/definitions.py[983-986]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
9. Uncopied GPU THINC constants 🐞 Bug ☼ Reliability
Description
m_thinc declares initialized constants (gq3_pts, gq3_wts, ln2) with
GPU_DECLARE(create=...), which (per the macro expansion) creates device storage without copying
host initialization values. On OpenACC builds this can leave these constants undefined on device,
producing incorrect MTHINC integrals.
Code

src/simulation/m_thinc.fpp[R30-35]

+    real(wp) :: gq3_pts(3) = [-5e-1_wp*0.7745966692414834_wp, 0._wp, 5e-1_wp*0.7745966692414834_wp]
+    !> Weights: 5/18, 8/18, 5/18
+    real(wp) :: gq3_wts(3) = [5._wp/18._wp, 8._wp/18._wp, 5._wp/18._wp]
+    !> ln(2)
+    real(wp) :: ln2 = 0.6931471805599453_wp
+    $:GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')
Evidence
The OpenACC ACC_DECLARE macro builds a !$acc declare directive from copyin and create
clauses; using only create means no copyin occurs. In m_thinc, the Gauss quadrature arrays and
ln2 are initialized on the host but only declare create is used and there is no later
GPU_UPDATE, so device-side values depend on compiler/runtime behavior and may be uninitialized.

src/simulation/m_thinc.fpp[28-35]
src/common/include/acc_macros.fpp[176-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`gq3_pts`, `gq3_wts`, and `ln2` are host-initialized module variables but are declared on the device with `GPU_DECLARE(create=...)`, which does not copy initial values to the device. This risks undefined values on OpenACC devices and wrong MTHINC integrals.
### Issue Context
`ACC_DECLARE` composes `!$acc declare` from `copyin` and `create`. Using only `create` allocates device memory without host->device initialization.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[28-35]
- src/common/include/acc_macros.fpp[176-191]
### Recommended fix
Prefer one of:
1. Change to a read-only copyin declaration:
- Replace `GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')` with `GPU_DECLARE(copyinReadOnly='[gq3_pts, gq3_wts, ln2]')`.
2. Or keep `create` but explicitly copy values once during initialization:
- Add `GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]')` in `s_initialize_thinc_module` (or another guaranteed initialization path) before any GPU kernels use them.
Either approach ensures deterministic device-side constants across OpenACC compilers/runtimes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit c183e43

Results up to commit c183e43


🐞 Bugs (6) 📘 Rule violations (2)


Action required
1. MTHINC normals ignore spacing 🐞 Bug ≡ Correctness ⭐ New
Description
s_compute_mthinc_normals computes the interface normal using unscaled index-space differences, so on
nonuniform/stretched grids the normal direction is wrong and MTHINC face averages are computed for
the wrong orientation. This can silently distort interface sharpening and fluxes in real runs
without crashing.
Code

src/simulation/m_thinc.fpp[R246-259]

+                    ac = v_vf(eqn_idx%adv%beg)%sf(j, k, l)
+
+                    if (ac >= ic_eps .and. ac <= 1._wp - ic_eps) then
+                        nr_x = (v_vf(eqn_idx%adv%beg)%sf(j + 1, k, l) - v_vf(eqn_idx%adv%beg)%sf(j - 1, k, l))*5e-1_wp
+
+                        nr_y = 0._wp
+                        if (n > 0) then
+                            nr_y = (v_vf(eqn_idx%adv%beg)%sf(j, k + 1, l) - v_vf(eqn_idx%adv%beg)%sf(j, k - 1, l))*5e-1_wp
+                        end if
+
+                        nr_z = 0._wp
+                        if (p > 0) then
+                            nr_z = (v_vf(eqn_idx%adv%beg)%sf(j, k, l + 1) - v_vf(eqn_idx%adv%beg)%sf(j, k, l - 1))*5e-1_wp
+                        end if
Evidence
The new normal computation uses centered differences multiplied by 0.5 with no dx/dy/dz (or
coordinate distance) scaling, while other physics in the codebase explicitly divides by dx/dy/dz
when forming gradients—indicating grid spacing is expected to matter.

src/simulation/m_thinc.fpp[246-259]
src/simulation/m_bubbles_EE.fpp[100-121]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/simulation/m_thinc.fpp:s_compute_mthinc_normals` computes MTHINC interface normals using raw index-space differences (no dx/dy/dz or coordinate-distance scaling). On nonuniform grids this produces incorrect normal directions and therefore incorrect MTHINC face-averaged volume fractions.

### Issue Context
Other gradient computations in the simulation divide by `dx`, `dy`, `dz`, so the codebase expects physical-space gradients.

### Fix Focus Areas
- src/simulation/m_thinc.fpp[246-283]

### What to change
- Replace the current centered differences with metric-scaled differences, e.g.:
 - `nr_x = (alpha(j+1)-alpha(j-1)) / (x_cc(j+1)-x_cc(j-1))` (or `/ (dx(j)+dx(j-1))` depending on grid definitions)
 - same for y/z using `y_cc`/`dy`, `z_cc`/`dz`.
- Keep the normalization to unit length afterward.
- Ensure the chosen metric works for cylindrical/axisymmetric setups if supported by the rest of the solver.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. int_comp absent on device 🐞 Bug ≡ Correctness
Description
In m_thinc:s_thinc_compression, the OpenACC-parallel kernel branches on int_comp (THINC vs MTHINC)
under default(present), but int_comp is not declared/created for the device in m_global_parameters,
risking an OpenACC runtime 'not present' failure in GPU builds. This risk is introduced because
int_comp is now referenced inside device code instead of only host-side conditionals.
Code

src/simulation/m_thinc.fpp[R380-383]

+                            if (aC >= ic_eps .and. aC <= 1._wp - ic_eps) then
+
+                                if (int_comp == 2 .and. n > 0) then
+                                    ! MTHINC: multi-dimensional face averages
Evidence
The project’s OpenACC loop macro defaults to default(present), meaning variables used inside the
GPU region must already be present on the device. The new THINC kernel references int_comp inside
the GPU loop, but the global parameter module’s GPU_DECLARE lists do not include int_comp, so
there is no clear path that makes it device-present.

src/common/include/acc_macros.fpp[132-158]
src/simulation/m_thinc.fpp[356-384]
src/simulation/m_global_parameters.fpp[205-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`int_comp` is referenced inside OpenACC GPU kernels under `default(present)`, but it is not explicitly declared/created on the device. This can produce OpenACC runtime failures (variable not present) in GPU builds.
### Issue Context
- `ACC_PARALLEL_LOOP` defaults to `default(present)`.
- `m_thinc:s_thinc_compression` branches on `int_comp` inside `$:GPU_PARALLEL_LOOP`.
- `m_global_parameters` contains the central `GPU_DECLARE` lists for globals, but does not include `int_comp`.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[356-384]
- src/simulation/m_global_parameters.fpp[205-216]
- src/common/include/acc_macros.fpp[132-158]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. vR densities use vL 🐞 Bug ≡ Correctness
Description
In m_thinc:s_thinc_compression (THINC path), rho_b and rho_e are computed only from vL_rs_vf_*
ratios and then applied to overwrite both vL_rs_vf_* and vR_rs_vf_* densities, discarding the
pre-compression vR reconstructed density/alpha ratios. Since WENO/MUSCL compute vL and vR using
different polynomials, this can make the right-face state inconsistent and distort fluxes.
Code

src/simulation/m_thinc.fpp[R453-473]

+                                        ! Save original density ratios before THINC overwrites them
+                                        rho_b = vL_rs_vf_${XYZ}$ (j, k, l, contxb)/vL_rs_vf_${XYZ}$ (j, k, l, advxb)
+                                        rho_e = vL_rs_vf_${XYZ}$ (j, k, l, contxe)/(1._wp - vL_rs_vf_${XYZ}$ (j, k, l, advxb))
+
+                                        ! Left reconstruction
+                                        aTHINC = qmin + 5e-1_wp*qmax*(1._wp + sgn*A)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxb) = rho_b*aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxe) = rho_e*(1._wp - aTHINC)
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
+
+                                        ! Right reconstruction
+                                        aTHINC = qmin + 5e-1_wp*qmax*(1._wp + sgn*(tanh(beta_eff) + A)/(1._wp + A*tanh(beta_eff)))
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxb) = rho_b*aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxe) = rho_e*(1._wp - aTHINC)
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
Evidence
The THINC branch explicitly derives density ratios solely from vL and then uses those same ratios to
set vR values. Separately, WENO reconstruction computes vL and vR using different coefficient sets
(cbL vs cbR), so vR generally differs from vL before compression; overwriting vR with vL-derived
ratios ignores that reconstruction.

src/simulation/m_thinc.fpp[453-473]
src/simulation/m_weno.fpp[729-766]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In the THINC (1D) compression path, right-face densities (`vR_rs_vf_*`) are overwritten using density ratios computed from the left-face reconstruction (`vL_rs_vf_*`). This discards the right-face reconstructed density/alpha ratios and can make the right state inconsistent.
### Issue Context
WENO/MUSCL reconstruct `vL` and `vR` with different coefficient sets, so they are not generally identical before compression. THINC should preserve each face’s density ratio (or use a consistent cell-averaged ratio) when updating densities alongside alpha.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[453-473]
- src/simulation/m_weno.fpp[729-766]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Multi-fluid THINC corruption 🐞 Bug ≡ Correctness
Description
s_thinc_compression updates only contxb/contxe and advxb/advxe (and forces `advxe = 1 -
aTHINC), which is only valid when num_fluids == 2`; with more fluids it will violate
volume-fraction normalization and corrupt partial densities. The case validator currently allows
int_comp values without checking num_fluids, so misconfigured multi-fluid runs won’t be blocked.
Code

src/simulation/m_thinc.fpp[R348-369]

+                                        rho1 = v_rs_ws(j, k, l, contxb)/aC
+                                        rho2 = v_rs_ws(j, k, l, contxe)/(1._wp - aC)
+
+                                        ! Left face (face_pos = -0.5)
+                                        aTHINC = f_mthinc_face_average(nh1, nh2, nh3, d_local, ic_beta, ${REC_DIR}$, -5e-1_wp, &
+                                                                       & num_dims)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxb) = rho1*aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, contxe) = rho2*(1._wp - aTHINC)
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vL_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
+
+                                        ! Right face (face_pos = +0.5)
+                                        aTHINC = f_mthinc_face_average(nh1, nh2, nh3, d_local, ic_beta, ${REC_DIR}$, 5e-1_wp, &
+                                                                       & num_dims)
+                                        if (aTHINC < ic_eps) aTHINC = ic_eps
+                                        if (aTHINC > 1._wp - ic_eps) aTHINC = 1._wp - ic_eps
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxb) = rho1*aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, contxe) = rho2*(1._wp - aTHINC)
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxb) = aTHINC
+                                        vR_rs_vf_${XYZ}$ (j, k, l, advxe) = 1._wp - aTHINC
Evidence
For non-IGR volume-fraction model runs, MFC stores **N** volume fractions for **N** fluids (not
N−1). However, THINC/MTHINC compression only overwrites the first and last
volume-fraction/continuity slots, leaving any intermediate fluids untouched and breaking the
sum-to-one constraint. The Python validator introduced/updated for integer int_comp does not
restrict this usage to num_fluids==2, so users can reach this incorrect code path.

src/simulation/m_global_parameters.fpp[889-904]
src/simulation/m_thinc.fpp[323-369]
toolchain/mfc/case_validator.py[370-384]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`int_comp` (THINC/MTHINC) compression currently assumes exactly 2 fluids by writing only `contxb/contxe` and `advxb/advxe` and setting `advxe = 1 - aTHINC`. When `num_fluids > 2`, this silently corrupts reconstructed state (volume fractions no longer sum to 1 and intermediate fluids are inconsistent).
### Issue Context
- `m_global_parameters` stores `num_fluids` volume fractions when `igr = F`.
- `s_thinc_compression` only updates the first+last fluid slots.
- The case validator does not prohibit `int_comp > 0` for `num_fluids != 2`.
### Fix Focus Areas
- toolchain/mfc/case_validator.py[370-384]
- src/simulation/m_thinc.fpp[323-412]
- src/simulation/m_start_up.fpp[926-931]
### Recommended fix
1. In `check_interface_compression`, fetch `num_fluids` and **prohibit** `int_comp > 0` unless `num_fluids == 2` (and optionally `model_eqns` is a volume-fraction-capable model).
2. Add a Fortran-side runtime guard (e.g., in startup/checker) that errors out if `int_comp > 0` and `num_fluids /= 2`, to protect non-toolchain execution paths.
3. (Optional) If multi-fluid compression is desired, generalize the algorithm to update all `adv_idx` and all `cont_idx` consistently rather than hard-coding `contxb/contxe` and `advxb/advxe`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
5. m_thinc indentation not 2-space 📘 Rule violation ⚙ Maintainability ⭐ New
Description
The newly added m_thinc module uses 4-space indentation, which conflicts with the required 2-space
indentation style. This reduces consistency and makes the codebase harder to maintain per the
project’s formatting conventions.
Code

src/simulation/m_thinc.fpp[R16-26]

+    use m_derived_types
+    use m_global_parameters
+    use m_helper
+
+#ifdef MFC_OpenACC
+    use openacc
+#endif
+
+    implicit none
+
+    private; public :: s_initialize_thinc_module, s_thinc_compression, s_compute_mthinc_normals, s_finalize_thinc_module
Evidence
PR Compliance ID 8 requires 2-space indentation for new/modified Fortran code. In the added
src/simulation/m_thinc.fpp module, statements (e.g., use ..., implicit none, and `private;
public :: ...`) are indented with 4 spaces.

CLAUDE.md
src/simulation/m_thinc.fpp[16-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Fortran module `src/simulation/m_thinc.fpp` uses 4-space indentation, but the style guide requires 2-space indentation.

## Issue Context
This PR introduces the new `m_thinc` module; as a new file, its formatting should match the required conventions.

## Fix Focus Areas
- src/simulation/m_thinc.fpp[16-26]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. THINC out-of-bounds risk 🐞 Bug ☼ Reliability ⭐ New
Description
s_thinc_compression unconditionally reads v_rs_ws(j±1,...) but the simulation executable reads
simulation.inp directly and does not enforce the toolchain validator’s constraints, so a user can
enable int_comp with first-order reconstruction and trigger out-of-bounds reads. This can cause
crashes or undefined behavior in release builds (no bounds checking).
Code

src/simulation/m_thinc.fpp[R316-319]

+                        do j = is1_d%beg, is1_d%end
+                            aCL = v_rs_ws(j - 1, k, l, eqn_idx%adv%beg)
+                            aC = v_rs_ws(j, k, l, eqn_idx%adv%beg)
+                            aCR = v_rs_ws(j + 1, k, l, eqn_idx%adv%beg)
Evidence
The new compression kernel reads j-1/j+1. Reconstruction bounds are shrunk by *_polyn; if
*_polyn==0 (first-order), bounds are not shrunk, making j±1 potentially outside the allocated
buffer. The Python validator prohibits int_comp != 0 with order-1 schemes, but the Fortran
simulation reads the namelist without enforcing those constraints, so the invalid configuration is
reachable by running the binary directly.

src/simulation/m_thinc.fpp[316-320]
src/simulation/m_rhs.fpp[1618-1633]
src/simulation/m_start_up.fpp[75-128]
toolchain/mfc/case_validator.py[386-402]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`s_thinc_compression` assumes a stencil and reads `v_rs_ws(j-1,...)` and `v_rs_ws(j+1,...)` for every `j` in `is1_d`. If the reconstruction polynomial degree is 0 (WENO/MUSCL order 1), `is1_d` is not shrunk and `j±1` can go out-of-bounds.

### Issue Context
The Python validator blocks `int_comp` with order-1 schemes, but `simulation` reads `simulation.inp` directly and does not enforce the same constraints, so users can still hit this path.

### Fix Focus Areas
- src/simulation/m_thinc.fpp[294-411]
- src/simulation/m_rhs.fpp[1618-1633]

### What to change
Implement a Fortran-side safety guard, e.g. one of:
1) Early return from `s_thinc_compression` when the active reconstruction has `*_polyn == 0`.
2) Tighten the j-loop bounds inside `s_thinc_compression` to avoid endpoints (e.g., iterate `j = is1_d%beg+1, is1_d%end-1` when needed).
3) Add an explicit runtime `s_mpi_abort`/`s_prohibit_abort` check during startup validating that `int_comp>0` implies reconstruction order > 1.

Prefer (1) or (3) if order-1 + compression is unsupported by design; prefer (2) if you want to allow it safely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Missing precheck/build/test evidence 📘 Rule violation ≡ Correctness
Description
The PR description does not document running ./mfc.sh precheck, ./mfc.sh build, or relevant
automated tests after adding a new Fortran module and changing parameter typing. Without this, CI
readiness and correctness of the change cannot be confirmed from the PR.
Code

src/simulation/m_thinc.fpp[R7-14]

+!> @brief THINC and MTHINC interface compression for volume fraction sharpening.
+!! THINC (int_comp=1): 1D directional interface compression applied after
+!! MUSCL/WENO reconstruction. Uses hyperbolic tangent profile per dimension.
+!! MTHINC (int_comp=2): Multi-dimensional THINC that reconstructs a tanh
+!! profile oriented along the interface normal (computed from the gradient
+!! of alpha), then integrates that profile over each cell face using
+!! Gaussian quadrature. A Newton iteration enforces the conservation
+!! constraint (cell-averaged alpha is preserved).
Evidence
Compliance ID 1 requires precheck, build, and relevant tests to be run and pass. The PR description
lists manual case runs/perf results but does not mention running the required commands, while the
diff shows substantial new Fortran functionality and a parameter type change that should be covered
by those checks.

CLAUDE.md
src/simulation/m_thinc.fpp[7-14]
toolchain/mfc/params/definitions.py[983-986]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR does not provide evidence that required CI-equivalent checks were run (`./mfc.sh precheck`, `./mfc.sh build`, and relevant tests).
## Issue Context
This PR adds a new Fortran module for THINC/MTHINC and changes the `int_comp` parameter type/handling; these changes should be validated by precheck/build and appropriate tests.
## Fix Focus Areas
- src/simulation/m_thinc.fpp[7-14]
- toolchain/mfc/params/definitions.py[983-986]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
8. Uncopied GPU THINC constants 🐞 Bug ☼ Reliability
Description
m_thinc declares initialized constants (gq3_pts, gq3_wts, ln2) with
GPU_DECLARE(create=...), which (per the macro expansion) creates device storage without copying
host initialization values. On OpenACC builds this can leave these constants undefined on device,
producing incorrect MTHINC integrals.
Code

src/simulation/m_thinc.fpp[R30-35]

+    real(wp) :: gq3_pts(3) = [-5e-1_wp*0.7745966692414834_wp, 0._wp, 5e-1_wp*0.7745966692414834_wp]
+    !> Weights: 5/18, 8/18, 5/18
+    real(wp) :: gq3_wts(3) = [5._wp/18._wp, 8._wp/18._wp, 5._wp/18._wp]
+    !> ln(2)
+    real(wp) :: ln2 = 0.6931471805599453_wp
+    $:GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')
Evidence
The OpenACC ACC_DECLARE macro builds a !$acc declare directive from copyin and create
clauses; using only create means no copyin occurs. In m_thinc, the Gauss quadrature arrays and
ln2 are initialized on the host but only declare create is used and there is no later
GPU_UPDATE, so device-side values depend on compiler/runtime behavior and may be uninitialized.

src/simulation/m_thinc.fpp[28-35]
src/common/include/acc_macros.fpp[176-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`gq3_pts`, `gq3_wts`, and `ln2` are host-initialized module variables but are declared on the device with `GPU_DECLARE(create=...)`, which does not copy initial values to the device. This risks undefined values on OpenACC devices and wrong MTHINC integrals.
### Issue Context
`ACC_DECLARE` composes `!$acc declare` from `copyin` and `create`. Using only `create` allocates device memory without host->device initialization.
### Fix Focus Areas
- src/simulation/m_thinc.fpp[28-35]
- src/common/include/acc_macros.fpp[176-191]
### Recommended fix
Prefer one of:
1. Change to a read-only copyin declaration:
 - Replace `GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')` with `GPU_DECLARE(copyinReadOnly='[gq3_pts, gq3_wts, ln2]')`.
2. Or keep `create` but explicitly copy values once during initialization:
 - Add `GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]')` in `s_initialize_thinc_module` (or another guaranteed initialization path) before any GPU kernels use them.
Either approach ensures deterministic device-side constants across OpenACC compilers/runtimes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 202d7da


🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0)


Action required
1. int_comp absent on device 🐞 Bug ⛯ Reliability
Details

@github-actions
Copy link
Copy Markdown

Claude Code Review

Head SHA: 202d7daedced880868e31df3614e3d4b0b742f80
Files changed: 81 (source: 15 key files + 66 golden-file renames/updates)

Key source files:

  • src/simulation/m_thinc.fpp (new, 499 lines)
  • src/simulation/m_rhs.fpp (+22/-1)
  • src/simulation/m_muscl.fpp (+1/-86)
  • src/simulation/m_weno.fpp (0/-8)
  • src/simulation/m_global_parameters.fpp (+3/-3)
  • src/simulation/m_mpi_proxy.fpp (+2/-2)
  • src/simulation/m_start_up.fpp (+3/0)
  • toolchain/mfc/case_validator.py, params/definitions.py, test/cases.py

Summary

  • Adds MTHINC (int_comp=2): multi-dimensional THINC using precomputed interface normals and Gaussian quadrature for face averages; Newton iteration enforces the conservation constraint per cell.
  • Fixes a bug in the original THINC right-reconstruction density (Fix MUSCL THINC right-state using already-overwritten left-state values #1181): the old code used the already-overwritten left-face advxb value when computing the right-face density ratio.
  • Refactors THINC out of m_muscl/m_weno into a dedicated m_thinc module called from m_rhs after reconstruction — enabling compression for both WENO and MUSCL.
  • Changes int_comp from logical to integer (0=off, 1=THINC, 2=MTHINC) throughout; correctly moves MPI broadcast from the logical list to the integer list in m_mpi_proxy.fpp.
  • Good numerics: numerically stable f_log_cosh for large arguments, analytical 1-D THINC integral, correct use of GPU Fypp macros throughout.

Findings

[Medium] Missing Fortran runtime check for int_comp range — src/simulation/m_checker.fpp

m_checker.fpp was not updated. Per project convention (CLAUDE.md, parameter-system.md), a Fortran-side @:PROHIBIT guard should be added alongside the Python-side check in case_validator.py:

@:PROHIBIT(int_comp < 0 .or. int_comp > 2, "int_comp must be 0 (off), 1 (THINC), or 2 (MTHINC)")

The Python validator catches this at case-setup time, but the Fortran check is the convention for parameters with constrained integer values.


[Medium] MTHINC silently degrades to 1-D THINC for 1-D cases — src/simulation/m_rhs.fpp:749, m_thinc.fpp

When int_comp=2 and n=0 (1-D):

  • m_rhs.fpp:749: if (int_comp == 2 .and. n > 0) is false → normals are never computed (mthinc_nhat stays zero).
  • Inside s_thinc_compression, if (int_comp == 2 .and. n > 0) is also false → the else branch (1-D THINC) executes silently.

Mathematically this is fine (MTHINC reduces to THINC in 1-D), but the user gets THINC behaviour when requesting MTHINC with no diagnostic. The old Python constraint that required recon_type = 2 was removed but no MTHINC+1D note was added. At minimum, a comment or case_validator.py note would help.


[Low] MTHINC skips monotonicity check — src/simulation/m_thinc.fpp (THINC vs MTHINC branch)

The 1-D THINC path guards with moncon > moncon_cutoff (checks (aCR - aC)*(aC - aCL) > cutoff), preventing compression in non-monotone regions. The MTHINC path only checks nh1² + nh2² + nh3² > 0.5 (valid normal exists). Non-monotone interface cells that still have a computable normal will get MTHINC applied — the ic_eps outer guard prevents pure-fluid cells, but the directional-monotonicity protection is absent. This is likely intentional (MTHINC's normal is a multi-dimensional signal), but should be confirmed or documented.


[Low] Two-fluid assumption is undocumented — src/simulation/m_thinc.fpp

Both THINC and MTHINC update only contxb, contxe, advxb, advxe — the indices for exactly two fluids. Cases with 3+ fluids (multiple adv_idx entries) receive silently incorrect density/volume-fraction reconstructions. The original m_muscl.fpp code had the same limitation, but a @:PROHIBIT(int_comp > 0 .and. num_fluids > 2, ...) or at least a comment in s_thinc_compression would prevent silent misuse.


[Positive] Correct THINC bug fix

The old right-reconstruction in m_muscl.fpp computed the density ratio as:

vR...(contxb) = vL...(contxb) / vL...(advxb) * aTHINC   ! Bug: advxb already overwritten

The new code in m_thinc.fpp correctly saves rho_b = vL.../vL...(advxb) and rho_e = vL.../((1-vL...(advxb))) before modifying the left face, then uses them for both left and right reconstructions. This is the right fix.


[Positive] Architecture improvement

Centralising interface compression into m_thinc called from m_rhs (post-reconstruction) rather than inside m_muscl/m_weno is cleaner and correctly extends THINC coverage to WENO reconstruction — verified by the new WENO + int_comp test cases added to cases.py.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a dedicated THINC/MTHINC interface-compression module and migrates the int_comp setting from a logical flag to an integer selector, while updating tests/docs/tooling accordingly.

Changes:

  • Added src/simulation/m_thinc.fpp implementing THINC (int_comp=1) and MTHINC (int_comp=2) and wired it into the reconstruction flow in m_rhs.
  • Replaced the old MUSCL/WENO-local interface-compression hooks by centralizing compression in RHS reconstruction; removed the MUSCL s_interface_compression implementation.
  • Updated toolchain parameter typing/validation and adjusted examples/docs/tests to use integer int_comp.

Reviewed changes

Copilot reviewed 42 out of 81 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/simulation/m_thinc.fpp New THINC/MTHINC implementation (including MTHINC normal computation and Newton solve).
src/simulation/m_rhs.fpp Calls MTHINC normal computation pre-split; runs compression post-reconstruction with NVTX ranges.
src/simulation/m_muscl.fpp Removes s_interface_compression and MUSCL-side compression call.
src/simulation/m_weno.fpp Removes MUSCL import and WENO-side compression call.
src/simulation/m_start_up.fpp Initializes/finalizes the new THINC module when int_comp > 0.
src/simulation/m_global_parameters.fpp Changes int_comp from logical to integer with updated defaults/comments.
src/simulation/m_mpi_proxy.fpp Broadcasts int_comp as an integer instead of logical.
toolchain/mfc/params/definitions.py Registers int_comp as INT instead of LOG.
toolchain/mfc/case_validator.py Validates int_comp is in {0,1,2}.
toolchain/mfc/test/cases.py Expands generated test matrix to include int_comp values {0,1,2}.
toolchain/mfc/params/descriptions.py Updates parameter description to document 0/1/2 meanings.
docs/documentation/case.md Updates docs to describe integer int_comp and mentions MTHINC.
docs/module_categories.json Adds m_thinc to documentation module grouping.
examples/*/case.py Updates examples from "T" to 1 for THINC.
tests/*/golden-metadata.txt Updates/adds golden metadata for the new/updated test set.

Comment thread toolchain/mfc/case_validator.py Outdated
Comment thread toolchain/mfc/test/cases.py Outdated
Comment thread docs/documentation/case.md Outdated
Comment thread src/simulation/m_thinc.fpp Outdated
Comment thread src/simulation/m_thinc.fpp Outdated
Comment thread src/simulation/m_thinc.fpp Outdated
Comment thread src/simulation/m_thinc.fpp Outdated
@wilfonba wilfonba marked this pull request as draft March 12, 2026 16:08
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

This pull request refactors interface compression from a boolean flag into an integer enumeration with three states: 0 (off), 1 (THINC), and 2 (MTHINC). The implementation moves compression logic from the MUSCL module into a dedicated new THINC module. Interface compression is now integrated into RHS calculations and invoked during variable reconstruction. Documentation, parameter definitions, validation logic, and example configurations are updated to reflect the new integer-based parameter type and semantic values. Test cases are expanded to exercise the new enumeration states.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'MTHINC' is concise and directly identifies the primary feature being added, though it lacks context about what MTHINC does or that it includes an interface compression feature.
Description check ✅ Passed PR description includes type of change, testing details with videos, performance measurements, and all required checklist items completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 411ddfaf-e410-411e-a688-203d6a1caf71

📥 Commits

Reviewing files that changed from the base of the PR and between 506c6f5 and 202d7da.

📒 Files selected for processing (81)
  • docs/documentation/case.md
  • docs/module_categories.json
  • examples/1D_sodshocktube_muscl/case.py
  • examples/2D_advection_muscl/case.py
  • examples/2D_riemann_test_muscl/case.py
  • examples/2D_shockdroplet_muscl/case.py
  • examples/3D_rayleigh_taylor_muscl/case.py
  • examples/3D_shockdroplet_muscl/case.py
  • src/simulation/m_global_parameters.fpp
  • src/simulation/m_mpi_proxy.fpp
  • src/simulation/m_muscl.fpp
  • src/simulation/m_rhs.fpp
  • src/simulation/m_start_up.fpp
  • src/simulation/m_thinc.fpp
  • src/simulation/m_weno.fpp
  • tests/09195EF4/golden-metadata.txt
  • tests/09195EF4/golden.txt
  • tests/0A362971/golden-metadata.txt
  • tests/0A362971/golden.txt
  • tests/11D609F6/golden-metadata.txt
  • tests/11D609F6/golden.txt
  • tests/1B300F28/golden-metadata.txt
  • tests/1F0C4A44/golden-metadata.txt
  • tests/1F0C4A44/golden.txt
  • tests/2C9844EF/golden-metadata.txt
  • tests/2C9844EF/golden.txt
  • tests/2D11A034/golden-metadata.txt
  • tests/2D11A034/golden.txt
  • tests/2E4AC44C/golden-metadata.txt
  • tests/2E4AC44C/golden.txt
  • tests/3ECA875A/golden-metadata.txt
  • tests/3ECA875A/golden.txt
  • tests/409B55DF/golden-metadata.txt
  • tests/409B55DF/golden.txt
  • tests/503EEFF7/golden-metadata.txt
  • tests/503EEFF7/golden.txt
  • tests/50EC2239/golden-metadata.txt
  • tests/50EC2239/golden.txt
  • tests/6102D509/golden-metadata.txt
  • tests/6102D509/golden.txt
  • tests/6775D763/golden-metadata.txt
  • tests/6775D763/golden.txt
  • tests/67C777D8/golden.txt
  • tests/7156050E/golden-metadata.txt
  • tests/7156050E/golden.txt
  • tests/845DC70C/golden-metadata.txt
  • tests/845DC70C/golden.txt
  • tests/885D5D8C/golden-metadata.txt
  • tests/885D5D8C/golden.txt
  • tests/8876692F/golden-metadata.txt
  • tests/8876692F/golden.txt
  • tests/8C42A56C/golden-metadata.txt
  • tests/8C42A56C/golden.txt
  • tests/A438B8CE/golden-metadata.txt
  • tests/A438B8CE/golden.txt
  • tests/A930AE61/golden-metadata.txt
  • tests/A930AE61/golden.txt
  • tests/AEE7A791/golden-metadata.txt
  • tests/AEE7A791/golden.txt
  • tests/B3C724B5/golden-metadata.txt
  • tests/B3C724B5/golden.txt
  • tests/B903D17E/golden-metadata.txt
  • tests/B903D17E/golden.txt
  • tests/C02D71EE/golden-metadata.txt
  • tests/C02D71EE/golden.txt
  • tests/C420EDF3/golden-metadata.txt
  • tests/C420EDF3/golden.txt
  • tests/CE35B602/golden-metadata.txt
  • tests/CE35B602/golden.txt
  • tests/E11FD23A/golden-metadata.txt
  • tests/E11FD23A/golden.txt
  • tests/F5FABAE9/golden-metadata.txt
  • tests/F5FABAE9/golden.txt
  • tests/FA4D8FEF/golden-metadata.txt
  • tests/FA4D8FEF/golden.txt
  • tests/FE9379D8/golden-metadata.txt
  • tests/FE9379D8/golden.txt
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/params/definitions.py
  • toolchain/mfc/params/descriptions.py
  • toolchain/mfc/test/cases.py
💤 Files with no reviewable changes (10)
  • tests/FA4D8FEF/golden.txt
  • tests/FA4D8FEF/golden-metadata.txt
  • tests/2C9844EF/golden-metadata.txt
  • src/simulation/m_weno.fpp
  • tests/0A362971/golden.txt
  • tests/1B300F28/golden-metadata.txt
  • tests/8876692F/golden-metadata.txt
  • tests/C02D71EE/golden-metadata.txt
  • tests/50EC2239/golden-metadata.txt
  • tests/0A362971/golden-metadata.txt

Comment thread docs/documentation/case.md Outdated
Comment thread docs/documentation/case.md Outdated
Comment thread src/simulation/m_global_parameters.fpp Outdated
Comment thread src/simulation/m_rhs.fpp
Comment thread src/simulation/m_start_up.fpp Outdated
Comment thread src/simulation/m_thinc.fpp Outdated
Comment thread src/simulation/m_thinc.fpp
Comment thread src/simulation/m_thinc.fpp Outdated
Comment thread toolchain/mfc/case_validator.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 83.49515% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.89%. Comparing base (36fc04c) to head (78062e3).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/simulation/m_thinc.fpp 84.74% 9 Missing and 18 partials ⚠️
src/simulation/m_muscl.fpp 66.66% 0 Missing and 2 partials ⚠️
src/simulation/m_weno.fpp 80.00% 0 Missing and 2 partials ⚠️
src/simulation/m_global_parameters.fpp 50.00% 0 Missing and 1 partial ⚠️
src/simulation/m_rhs.fpp 88.88% 0 Missing and 1 partial ⚠️
src/simulation/m_start_up.fpp 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1303      +/-   ##
==========================================
+ Coverage   64.75%   64.89%   +0.13%     
==========================================
  Files          71       72       +1     
  Lines       18721    18878     +157     
  Branches     1551     1571      +20     
==========================================
+ Hits        12123    12251     +128     
- Misses       5640     5651      +11     
- Partials      958      976      +18     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Copy Markdown

Claude Code Review

Head SHA: 85acf5a

Files changed: 81 (key source files: m_thinc.fpp [new, +499], m_muscl.fpp [-86], m_weno.fpp [-8], m_rhs.fpp [+22], m_global_parameters.fpp, m_mpi_proxy.fpp, m_start_up.fpp, toolchain/mfc/params/definitions.py, toolchain/mfc/case_validator.py, toolchain/mfc/test/cases.py)


Summary

  • Adds MTHINC (int_comp=2), a multi-dimensional THINC variant using interface normals + Gaussian quadrature face integration; THINC becomes int_comp=1 (integer, was boolean).
  • Moves interface compression from per-solver modules (m_muscl.fpp, m_weno.fpp) into a new unified m_thinc module called from m_rhs.fpp after any reconstruction type, enabling THINC/MTHINC with WENO too.
  • Fixes the Fix MUSCL THINC right-state using already-overwritten left-state values #1181 bug: old THINC right-reconstruction density scaling read from already-overwritten vL_rs_vf values; new code saves rho_b/rho_e before any overwrite.
  • Correct MPI broadcast migration: int_comp moved from logical to integer broadcast list in m_mpi_proxy.fpp.
  • New and regenerated golden files cover THINC + MTHINC for WENO and MUSCL in 2D/3D.

Findings

[Medium] n > 0 guard for MTHINC silently degrades x-z plane 2D simulations
m_rhs.fpp:+749 and m_thinc.fpp:+142:

if (int_comp == 2 .and. n > 0) then   ! MTHINC guard

n is the number of y-direction cells. For a 2D simulation in the x-z plane (n == 0, p > 0), this guard evaluates to .false., so s_compute_mthinc_normals is never called and s_thinc_compression silently falls to the 1D-THINC branch — even though the problem is multi-dimensional. Since standard MFC 2D problems use x-y (n > 0), this is low-risk in practice, but the guard would be more correct as num_dims > 1.

[Medium] Newton solver f_mthinc_solve_d has no bounds clamping or convergence fallback
m_thinc.fpp:+174-188:

do iter = 1, 30
    ...
    d = d - residual/dV
end do

If convergence fails (e.g., degenerate normal near a triple point or machine-epsilon gradient), the function returns an unclamped d after 30 iterations without any diagnostic. A large dtanh(beta*(n·xi + d)) ≈ ±1 everywhere, collapsing the face average to 0 or 1. Adding a clamp (e.g., d = max(-50._wp/ic_beta, min(50._wp/ic_beta, d))) after the Newton step would prevent potential NaN propagation downstream.

[Low] Removed recon_type == 2 constraint without replacement
toolchain/mfc/case_validator.py:+353:
The old validator prohibited int_comp with non-MUSCL reconstruction. The new constraint only validates the integer value range. Since WENO + THINC/MTHINC is now intentionally supported (test cases confirm it), there is no correctness issue — but a note in case_validator.py or docs clarifying that both WENO and MUSCL are valid would help users.

[Low] Unused allocations for MTHINC in 1D
m_thinc.fpp:+250-263:
When int_comp == 2 and num_dims == 1, s_initialize_thinc_module allocates mthinc_nhat and mthinc_d on the GPU, but s_compute_mthinc_normals is never called (guarded by n > 0) and s_thinc_compression falls to the 1D-THINC branch (the MTHINC branch also checks n > 0 and skips). The allocations are unnecessary for 1D MTHINC. This is a minor memory overhead, not a correctness issue.

[Info] Bug fix for #1181 confirmed correct
Old m_muscl.fpp right-reconstruction:

! BUG: vL_rs_vf_XYZ(advxb) already overwritten by left reconstruction above
vR_rs_vf_XYZ(contxb) = vL_rs_vf_XYZ(contxb) / vL_rs_vf_XYZ(advxb) * aTHINC

New code correctly saves rho_b = contxb/advxb and rho_e = contxe/(1-advxb) before the left overwrite, then uses those saved values for both left and right reconstructions. The fix is correct.


Overall the implementation is well-structured. The n > 0 MTHINC guard and Newton solver robustness are the two items worth addressing before merge.

@wilfonba wilfonba changed the title MTHINC Interface Compression MTHINC Mar 15, 2026
@sbryngelson sbryngelson force-pushed the MTHINC branch 2 times, most recently from 2e0268f to 617f985 Compare March 16, 2026 14:20
@github-actions
Copy link
Copy Markdown

Claude Code Review

Incremental review from: (first review)
Head SHA: b4e110c

New findings:

1. Critical — GPU coherence: int_comp, ic_eps, ic_beta missing GPU_DECLARE and GPU_UPDATE

All three variables are read inside GPU_PARALLEL_LOOP regions in src/simulation/m_thinc.fpp (e.g., s_thinc_compression uses int_comp at the loop body branch, ic_eps and ic_beta for THINC/MTHINC profile computation) but none appear to have:

  • a $:GPU_DECLARE(create='[...]') in src/simulation/m_global_parameters.fpp
  • a $:GPU_UPDATE(device='[...]') in s_initialize_gpu_vars in src/simulation/m_start_up.fpp

By contrast, the analogous scalar weno_eps has $:GPU_DECLARE(create='[avg_state,mp_weno,weno_eps,teno_CT,hypoelasticity]'). On OpenACC or OpenMP target offload builds, the GPU will see uninitialized/stale values for int_comp, ic_eps, and ic_beta, causing either no compression (if int_comp reads as 0 on device) or silently wrong results. CPU-only builds are unaffected, which is likely why tests passed.

Fix: Add to src/simulation/m_global_parameters.fpp:

$:GPU_DECLARE(create='[int_comp,ic_eps,ic_beta]')

And add to s_initialize_gpu_vars in src/simulation/m_start_up.fpp:

$:GPU_UPDATE(device='[int_comp,ic_eps,ic_beta]')

2. Moderate — MTHINC density reconstruction silently skips intermediate fluid components for num_fluids > 2

In s_thinc_compression (src/simulation/m_thinc.fpp), the MTHINC path reconstructs face densities using only contxb and contxe:

rho1 = v_vf(contxb)%sf(ix, iy, iz)/aC
rho2 = v_vf(contxe)%sf(ix, iy, iz)/(1._wp - aC)

This implicitly assumes exactly 2 fluids. For num_fluids > 2, the intermediate partial density components (contxb+1 through contxe-1) are left unmodified by MTHINC, producing physically inconsistent states where updated volume fractions no longer match unupdated partial densities.

There is no constraint in toolchain/mfc/case_validator.py or in the Fortran checker (src/simulation/m_checker.fpp) preventing int_comp > 0 with num_fluids > 2.

Fix: Either extend the density reconstruction to loop over all fluids from contxb to contxe, or guard in the validator:

self.prohibit(int_comp > 0 and num_fluids > 2,
    "int_comp (THINC/MTHINC) only supports num_fluids <= 2")

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 18, 2026

<!-- claude-review: thread=primary; reviewed_sha=0ed0772ee8995e5752187f6d67d035a6bc49b556; mode=incremental -->

@sbryngelson
Copy link
Copy Markdown
Member

@wilfonba can you re-run a few sanity checks to make sure this works after i made a bunch of changes? then i guess we can mark it ready to review, which ensures it works on frontier/phoenix/whatever. then merge.

@wilfonba
Copy link
Copy Markdown
Contributor Author

@wilfonba can you re-run a few sanity checks to make sure this works after i made a bunch of changes? then i guess we can mark it ready to review, which ensures it works on frontier/phoenix/whatever. then merge.

Yep. I'll send you a message and open it as ready to review when I've taken care of that.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

Claude Code Review

Head SHA: c183e43

Files changed:

  • 20
  • src/simulation/m_thinc.fpp (new file)
  • src/simulation/m_muscl.fpp
  • src/simulation/m_weno.fpp
  • src/simulation/m_rhs.fpp
  • src/simulation/m_global_parameters.fpp
  • src/simulation/m_mpi_proxy.fpp
  • src/simulation/m_start_up.fpp
  • src/common/m_constants.fpp
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/params/definitions.py

Findings

GPU device data not initialized for MTHINC quadrature constants (m_thinc.fpp)

gq3_pts, gq3_wts, and ln2 are mutable module-level variables (not parameter) with Fortran initializers:

real(wp) :: gq3_pts(3) = [-5e-1_wp*0.7745966692414834_wp, 0._wp, 5e-1_wp*0.7745966692414834_wp]
real(wp) :: gq3_wts(3) = [5._wp/18._wp, 8._wp/18._wp, 5._wp/18._wp]
real(wp) :: ln2 = 0.6931471805599453_wp
$:GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')

GPU_DECLARE(create=...) allocates device memory but does not copy host values to the device (this is the semantics of !$acc declare create). s_initialize_thinc_module() only allocates mthinc_nhat and mthinc_d, with no GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]') call anywhere.

All three are read inside $:GPU_ROUTINE(parallelism='[seq]') device functions (f_log_cosh, f_mthinc_volume_integral, f_mthinc_volume_integral_dd, f_mthinc_face_average) which are called from within the GPU_PARALLEL_LOOP regions in s_compute_mthinc_normals and s_thinc_compression. On any GPU build with int_comp=2, the Newton iteration in f_mthinc_solve_d and all Gauss quadrature will read uninitialized device memory, producing silent garbage results.

Fix: add $:GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]') inside s_initialize_thinc_module().

@sbryngelson sbryngelson force-pushed the MTHINC branch 4 times, most recently from 416d0ad to 755fba3 Compare March 26, 2026 19:46
@wilfonba
Copy link
Copy Markdown
Contributor Author

wilfonba commented Apr 3, 2026

@sbryngelson, I believe this is ready to merge. The failed benchmark shows good performance if you look at the generated comment. There was just some sort of error with the Slurm job. I can rerun it still if you want though.

@sbryngelson
Copy link
Copy Markdown
Member

Code review

Found 2 issues:

  1. Missing num_fluids == 2 constraint in check_interface_compression(). The THINC/MTHINC code in s_thinc_compression hardcodes updates to only contxb/contxe and advxb/advxe, and sets the second volume fraction to 1 - alpha_1. This only works for exactly 2 fluids. With num_fluids > 2, intermediate partial densities and volume fractions are silently left unmodified, violating volume fraction normalization. The existing surface_tension constraint follows the correct pattern: self.prohibit(surface_tension and num_fluids != 2, ...). (CLAUDE.md says "Every new parameter MUST be added in at least 3 places (4 if it has constraints)")

def check_interface_compression(self):
"""Check constraints regarding interface compression"""
int_comp = self.get("int_comp", 0)
n = self.get("n", 0)
self.prohibit(int_comp not in [0, 1, 2], "int_comp must be 0 (off), 1 (THINC), or 2 (MTHINC)")
self.prohibit(int_comp == 2 and n == 0, "int_comp = 2 (MTHINC) requires at least 2D (n > 0)")
recon_type = self.get("recon_type", 1)
if recon_type == 1: # WENO
weno_order = self.get("weno_order")
self.prohibit(weno_order == 1 and int_comp != 0, "int_comp must be 0 (off) when weno_order = 1")
elif recon_type == 2: # MUSCL
muscl_order = self.get("muscl_order")

  1. Init/finalize guard asymmetry for THINC module in m_start_up.fpp. s_initialize_thinc_module() is called inside if (.not. igr .or. dummy) (line 917), but s_finalize_thinc_module() is inside else of if (igr) (line 1085). When igr=.true. and dummy=.true.: init runs (allocates mthinc_nhat, mthinc_d), but finalize never runs because it is in the else branch which requires igr=.false.. This is a memory leak on the IGR dummy path. (CLAUDE.md says "Every @:ALLOCATE(...) MUST have a matching @:DEALLOCATE(...)")

if (igr .or. dummy) then
call s_initialize_igr_module()
end if
if (.not. igr .or. dummy) then
if (recon_type == WENO_TYPE) then
call s_initialize_weno_module()
else if (recon_type == MUSCL_TYPE) then
call s_initialize_muscl_module()
end if
if (int_comp > 0) call s_initialize_thinc_module()
call s_initialize_cbc_module()
call s_initialize_riemann_solvers_module()
end if

if (igr) then
call s_finalize_igr_module()
else
call s_finalize_cbc_module()
call s_finalize_riemann_solvers_module()
if (recon_type == WENO_TYPE) then
call s_finalize_weno_module()
else if (recon_type == MUSCL_TYPE) then
call s_finalize_muscl_module()
end if
if (int_comp > 0) call s_finalize_thinc_module()
end if

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@wilfonba
Copy link
Copy Markdown
Contributor Author

wilfonba commented Apr 5, 2026

This was a good catch by Claude. A bunch of the tests for M/THINC were one fluid. I'll work on removing the existing garbage tests and creating new ones

@sbryngelson sbryngelson marked this pull request as draft April 16, 2026 14:00
- case.md: add muscl_eps row/description alongside int_comp (MTHINC version)
- m_global_parameters.fpp: keep int_comp as integer (0/1/2), add muscl_eps; keep GPU_DECLARE; both GPU_UPDATEs
- m_mpi_proxy.fpp: broadcast both int_comp and collision_model as integers
- case_validator.py: keep recon_type validation from master; drop stale int_comp=boolean check (superseded by check_interface_compression)
@sbryngelson sbryngelson marked this pull request as ready for review May 4, 2026 14:14
sbryngelson
sbryngelson previously approved these changes May 4, 2026
Copy link
Copy Markdown
Member

@sbryngelson sbryngelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good. Both issues from the April 5 review are fixed (num_fluids constraint and init/finalize guard). Merge conflicts resolved.

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 4, 2026

Persistent review updated to latest commit c183e43

Comment on lines +246 to +259
ac = v_vf(eqn_idx%adv%beg)%sf(j, k, l)

if (ac >= ic_eps .and. ac <= 1._wp - ic_eps) then
nr_x = (v_vf(eqn_idx%adv%beg)%sf(j + 1, k, l) - v_vf(eqn_idx%adv%beg)%sf(j - 1, k, l))*5e-1_wp

nr_y = 0._wp
if (n > 0) then
nr_y = (v_vf(eqn_idx%adv%beg)%sf(j, k + 1, l) - v_vf(eqn_idx%adv%beg)%sf(j, k - 1, l))*5e-1_wp
end if

nr_z = 0._wp
if (p > 0) then
nr_z = (v_vf(eqn_idx%adv%beg)%sf(j, k, l + 1) - v_vf(eqn_idx%adv%beg)%sf(j, k, l - 1))*5e-1_wp
end if
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Mthinc normals ignore spacing 🐞 Bug ≡ Correctness

s_compute_mthinc_normals computes the interface normal using unscaled index-space differences, so on
nonuniform/stretched grids the normal direction is wrong and MTHINC face averages are computed for
the wrong orientation. This can silently distort interface sharpening and fluxes in real runs
without crashing.
Agent Prompt
### Issue description
`src/simulation/m_thinc.fpp:s_compute_mthinc_normals` computes MTHINC interface normals using raw index-space differences (no dx/dy/dz or coordinate-distance scaling). On nonuniform grids this produces incorrect normal directions and therefore incorrect MTHINC face-averaged volume fractions.

### Issue Context
Other gradient computations in the simulation divide by `dx`, `dy`, `dz`, so the codebase expects physical-space gradients.

### Fix Focus Areas
- src/simulation/m_thinc.fpp[246-283]

### What to change
- Replace the current centered differences with metric-scaled differences, e.g.:
  - `nr_x = (alpha(j+1)-alpha(j-1)) / (x_cc(j+1)-x_cc(j-1))` (or `/ (dx(j)+dx(j-1))` depending on grid definitions)
  - same for y/z using `y_cc`/`dy`, `z_cc`/`dz`.
- Keep the normalization to unit length afterward.
- Ensure the chosen metric works for cylindrical/axisymmetric setups if supported by the rest of the solver.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 4, 2026

Persistent review updated to latest commit c183e43

@sbryngelson
Copy link
Copy Markdown
Member

Claude Code Review

PR #1303 — MTHINC

This PR adds MTHINC interface compression and refactors THINC into m_thinc.fpp. The Python-side validation is thorough and the THINC overwrite bug fix is correct. Three issues require attention before merge.


Critical Issues

1. GPU device memory never initialized for Gauss-quadrature constants — MTHINC produces garbage on GPU
src/simulation/m_thinc.fpp lines 24–27

real(wp) :: gq3_pts(3) = [...]
real(wp) :: gq3_wts(3) = [...]
real(wp) :: ln2 = 0.6931471805599453_wp
$:GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')

GPU_DECLARE(create=...) allocates uninitialized device memory. It does NOT copy host values to the device. There is no $:GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]') call anywhere in the file — not in s_initialize_thinc_module, not in s_thinc_compression, nowhere.

These constants are read inside GPU seq device routines: f_log_cosh (uses ln2), f_mthinc_volume_integral and f_mthinc_face_average (use gq3_pts, gq3_wts). On GPU, all three read undefined values, silently producing wrong results for every MTHINC kernel invocation.

The codebase pattern is clear: m_weno.fpp declares arrays with GPU_DECLARE(create=...) and then calls $:GPU_UPDATE(device=...) inside s_weno before GPU loops. Fix — add to s_initialize_thinc_module:

subroutine s_initialize_thinc_module()
    $:GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]')
    ...
end subroutine

Important Issues

2. MTHINC normals ignore grid spacing — incorrect on non-uniform and anisotropic grids
src/simulation/m_thinc.fpp ~lines 165–174 (s_compute_mthinc_normals)

nr_x = (v_vf(eqn_idx%adv%beg)%sf(j+1,k,l) - v_vf(eqn_idx%adv%beg)%sf(j-1,k,l))*5e-1_wp
nr_y = (v_vf(eqn_idx%adv%beg)%sf(j,k+1,l) - v_vf(eqn_idx%adv%beg)%sf(j,k-1,l))*5e-1_wp

No division by dx, dy, dz. The factor 0.5 assumes unit spacing in every direction. On non-uniform grids or whenever dx ≠ dy (common in 2D/3D cases), the computed normal direction is distorted by the grid aspect ratio and does not represent the physical interface orientation, degrading MTHINC to worse-than-1D behavior.

Fix:

nr_x = (alpha_jp1 - alpha_jm1) / (x_cc(j+1) - x_cc(j-1))
nr_y = (alpha_kp1 - alpha_km1) / (y_cc(k+1) - y_cc(k-1))
nr_z = (alpha_lp1 - alpha_lm1) / (z_cc(l+1) - z_cc(l-1))

3. 4-space indentation throughout m_thinc.fpp — project standard is 2-space

Project standard enforced by ./mfc.sh format. Run ./mfc.sh format -j 8 before merge.


Strengths

  • Python-side validation in case_validator.py is comprehensive: enforces num_fluids=2, n>0 for MTHINC, and WENO/MUSCL order ≥ 3 requirements.
  • GPU_DECLARE(create=...) + GPU_UPDATE(device=...) for int_comp, ic_eps, ic_beta in m_global_parameters.fpp follows the correct pattern.
  • The THINC overwrite bug fix is correct: rho_b/rho_e are read from unmodified v_rs_ws before any writes to vL/vR.
  • @:ALLOCATE/@:DEALLOCATE pairing for mthinc_nhat and mthinc_d is correct and symmetric.
  • MPI: int_comp correctly moved from the logical broadcast list to the integer list in m_mpi_proxy.fpp.
  • GPU_ROUTINE(parallelism='[seq]') correctly applied to all device functions.

Comment thread src/simulation/m_weno.fpp
Comment on lines +1386 to 1395
if (int_comp > 0 .and. v_size >= eqn_idx%adv%end) then
call nvtxStartRange("WENO-INTCOMP")
#:for WENO_DIR, XYZ in [(1, 'x'), (2, 'y'), (3, 'z')]
if (weno_dir == ${WENO_DIR}$) then
call s_thinc_compression(v_rs_ws_${XYZ}$, vL_rs_vf_x, vL_rs_vf_y, vL_rs_vf_z, vR_rs_vf_x, vR_rs_vf_y, &
& vR_rs_vf_z, weno_dir, is1_weno, is2_weno, is3_weno)
end if
#:endfor
call nvtxEndRange()
end if
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Compression skipped on split recon 🐞 Bug ≡ Correctness

s_thinc_compression indexes reconstruction arrays using global eqn_idx%cont/%adv indices, but
reconstruction is frequently invoked on reindexed slices of the variable vector; the new guard
v_size >= eqn_idx%adv%end causes interface compression to be silently skipped in common
split-reconstruction paths (e.g., viscous/surface-tension branches), meaning int_comp has no
effect in those runs.
Agent Prompt
### Issue description
Interface compression is called from WENO/MUSCL on the reconstruction-time variable slice, but `s_thinc_compression` uses global `eqn_idx` indices (cont/adv) to index into those slice-local arrays. The current workaround guard (`v_size >= eqn_idx%adv%end`) prevents out-of-bounds, but also disables interface compression in split reconstruction paths (e.g., viscous/surface-tension branches in `m_rhs`).

### Issue Context
`m_rhs` reconstructs different variable ranges in separate calls depending on physics settings; interface compression needs cont+adv together (and needs correct indexing) to function.

### Fix Focus Areas
- src/simulation/m_weno.fpp[1386-1395]
- src/simulation/m_muscl.fpp[213-221]
- src/simulation/m_rhs.fpp[657-696]
- src/simulation/m_rhs.fpp[1609-1648]
- src/simulation/m_thinc.fpp[294-411]

### Recommended fix options (pick one)
1) **Move compression to `m_rhs` (preferred for correctness):**
   - Always reconstruct cont+adv in the same call when `int_comp>0` (even if other variables are reconstructed separately).
   - Then call `s_thinc_compression` with full (global-indexed) arrays, avoiding slice reindexing entirely.

2) **Make `s_thinc_compression` slice-aware:**
   - Pass an `iv_beg` (global offset) into WENO/MUSCL and then into `s_thinc_compression`.
   - Compute local indices: `adv_local = eqn_idx%adv%beg - iv_beg + 1`, similarly for `cont_local`.
   - Use these local indices for `v_rs_ws` and `vL/vR` access.
   - Update the call guard to check presence via offsets rather than `v_size >= eqn_idx%adv%end`.

Add/extend a test where `Re_size != 0` (or surface tension path) and `int_comp=1/2` to ensure compression is actually applied (not silently skipped).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants